#include "imageshared.h"
#include "MyBIGNUM.h"
#include "myecpoint.h"
#include "BitOperation.h"
#include "onewayfunction.h"
#include "steganographyOnImage.h"
#include <QDebug>
#include <QHash>
#include <QStringList>
#include <openssl/rand.h>
#include <math.h>
#include <QFile>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#define min(x,y) (((x)<(y))?(x):(y))
#define max(x,y) (((x)>(y))?(x):(y))
imageShared::imageShared(std::shared_ptr<EC_GROUP> group)
    :m_group(group)
    ,m_point(group)
{

}

bool imageShared::setImage(QString fileName)
{
    m_image=QImage(fileName);
    return !m_image.isNull();
}

QVector<unsigned int> imageShared::getRofSet(unsigned int setCount)
{
    QVector<unsigned int> ret;
    for(unsigned int i=0;i<setCount;++i){
        ret.push_back(rand()%(setCount*3));
    }
    return ret;
}

QString imageShared::getPubInfo(
    const QVector<QByteArrayList>& shadows,     //影子
    const QVector<unsigned int>& setR, //授权集合标识
    const QVector<QStringList>&  set   //授权集合
    )
{
    int sqjhCount=set.count();
    auto imageData=splitImage();
    MyBIGNUM x;
    QJsonObject jsonObj;
    jsonObj.insert("m_k",m_k);
    jsonObj.insert("m_N",m_N);
    jsonObj.insert("depth",m_image.depth());
    jsonObj.insert("w",m_image.width());
    jsonObj.insert("h",m_image.height());
    jsonObj.insert("format",m_image.format());
    int sizeInBytes=m_image.sizeInBytes();
    for(int i=0;i<sqjhCount;++i){//授权集合
        //添加r
        int n=set[i].count();
        QJsonArray jsonArr;
        jsonArr.append(QJsonValue{(int)setR[i]});
        int pos=0;
        for(int j=0;j<m_N;++j){  //秘密组
            auto select_j0=imageData[pos];
            pos+=1;
            QByteArray T=select_j0;
            //计算T=f
            for(int p=0;p<n;++p){//授权集合里的成员
                auto s=shadows[set[i][p].toInt()-1][j];
                x.setNumFromUChar((uchar*)s.data(), s.length());
                QByteArray frx=OneWayFunction::ecFunc(setR[i],x,m_point,m_group.get());
                T=UCharXOR(T,frx);
            }
            QJsonArray jsonTS;
            jsonTS.append(QJsonValue{QString(T.toBase64())});
            for(int p=1;p<m_L&&(sizeInBytes/(m_aSelectBits/8))>pos;++p){
                auto select_jp=imageData[pos];
                pos+=1;
                jsonTS.append(QJsonValue(QString(UCharXOR(select_j0,select_jp).toBase64())));
            }
            jsonArr.append(jsonTS);
        }
        qRgb(1,2,3);
        jsonObj.insert(QString("setR")+QString::number(i), jsonArr);
    }
    QJsonDocument Document{jsonObj};
    return Document.toJson();
}

bool imageShared::savePubInfo(QString savePos,QString json)
{
    QFile file{savePos};
    if (!file.open(QIODevice::ReadWrite))
    {
        return false;
    }
    int cnt=file.write(json.toLocal8Bit());
    file.close();
    return cnt==json.size();
}

bool imageShared::saveShadow(const QVector<QByteArrayList>& shadow, const QString &savePos, const QTableWidget &cyz, SaveMethod method, QString carImagePos)
{
    QByteArrayList newshadow=getShadow(shadow);
    switch (method) {
    case TXT:
        return saveShadowToTxt(newshadow,savePos,cyz);
    case IMA:
        return saveShadowToImage(newshadow,savePos,cyz);
    case LSB:
        return saveShadowToLSB(newshadow,savePos,cyz,carImagePos);
    case EMD:
        return saveShadowToEMD(newshadow,savePos,cyz,carImagePos);
    default:
        break;
    }
    return false;
}

///////////////////////////////////////////////////////////
/// \brief imageShared::recover
/// \param f_r_xs f(r,x)的结果集，
/// \param pubInfoFileName 公共文件路径
/// \param setR 访问结构编号
/// \return 返回的图像
QImage imageShared::recover(const QVector<QByteArrayList> &f_r_xs, const QString& pubInfoFileName, int setR, void(*logFun)(QString))
{
    QFile jsonFile{pubInfoFileName};
    if (!jsonFile.open(QIODevice::ReadOnly)) {
        if(logFun)
            logFun("Failed to open file:"+pubInfoFileName);
        return QImage();
    }
    QByteArray json = jsonFile.readAll();
    jsonFile.close();
    QJsonParseError error;
    QJsonDocument doc=QJsonDocument::fromJson(json,&error);
    if (doc.isNull()) {
        if(logFun)
            logFun("Failed to parse JSON:" + error.errorString());
        return QImage();
    }
    int w=doc["w"].toInt(),h=doc["h"].toInt();
    if(w==0||h==0){
        return QImage();
    }
    QJsonArray TandS=doc["setR"+QString::number(setR)].toArray();
    QByteArray imadeData;
    int dep=doc["depth"].toInt();
    int aSelectBytes = dep*doc["m_k"].toInt()/8;
    for(int i=0;i<f_r_xs[0].size();++i){//组数
        auto ss=TandS[i+1].toArray();
        QByteArray si1=QByteArray::fromBase64(ss[0].toString().toLocal8Bit());
        for(int j=0;j<f_r_xs.size();++j){//参与者
            auto s=f_r_xs[j][i];
            si1=UCharXOR(si1,s);
        }//
        si1.resize(aSelectBytes);
        imadeData.append(si1);
        for(int j=1;j<ss.size();++j){
            auto d=QByteArray::fromBase64(ss[j].toString().toLocal8Bit());
            imadeData.append(UCharXOR(si1,d));
        }
    }
    QImage image{w, h, (QImage::Format)doc["format"].toInt()};
    memcpy(image.bits(), imadeData.data(), w*h*(dep/8));
    return image;
}

QImage imageShared::recover(const QVector<QByteArrayList> &f_r_xs, const QJsonObject &obj, int setR)
{
    int w=obj["w"].toInt(),h=obj["h"].toInt();
    if(w==0||h==0){
        return QImage();
    }
    QJsonArray TandS=obj["setR"+QString::number(setR)].toArray();
    QByteArray imadeData;
    int dep=obj["depth"].toInt();
    int aSelectBytes = dep*obj["m_k"].toInt()/8;
    for(int i=0;i<f_r_xs[0].size();++i){//组数
        auto ss=TandS[i+1].toArray();
        QByteArray si1=QByteArray::fromBase64(ss[0].toString().toLocal8Bit());
        for(int j=0;j<f_r_xs.size();++j){//参与者
            auto s=f_r_xs[j][i];
            si1=UCharXOR(si1,s);
        }//
        si1.resize(aSelectBytes);
        imadeData.append(si1);
        for(int j=1;j<ss.size();++j){
            auto d=QByteArray::fromBase64(ss[j].toString().toLocal8Bit());
            imadeData.append(UCharXOR(si1,d));
        }
    }
    QImage image{w, h, (QImage::Format)obj["format"].toInt()};
    memcpy(image.bits(), imadeData.data(), w*h*(dep/8));
    return image;
}

QVector<QByteArrayList> imageShared::getShadow(unsigned int participatorCount, int L)
{
    if(!getSystemParameters(L)){
        return QVector<QByteArrayList>();
    }
    this->participatorCount=participatorCount;
    QVector<QByteArrayList> ret{participatorCount};
    auto nn=m_aSelectBits/8;
    for(unsigned int i=0;i<participatorCount;++i){
        for(int j=0;j<m_N;++j){
            unsigned char *buf=new unsigned char[nn];
            memset(buf,0,nn);
            int ok=RAND_bytes(buf,nn);
            if(ok){
                ret[i].push_back(QByteArray((char*)buf,nn));
            }
            delete[] buf;
        }
    }
    return ret;
}

QByteArrayList imageShared::getShadow(const QVector<QByteArrayList>& shadow)
{
    QByteArrayList ret=QByteArrayList(shadow.size());
    for(int i=0;i<shadow.size();++i){
        for(int j=0;j<shadow[i].size();++j){
            ret[i].append(shadow[i][j]);
        }
    }
    return ret;
}

QByteArrayList imageShared::splitImage()
{
    int pos=0;
    QByteArrayList ans{};
    auto data=m_image.bits();
    auto datalen=m_image.sizeInBytes();
    auto sublen=m_aSelectBits/8;
    int selectCount=datalen/sublen+(datalen%sublen ? 1:0);
    for(int i=0;i<selectCount;++i){
        int len=(pos+sublen)>datalen?(datalen-pos):sublen;
        ans.push_back(QByteArray((const char*)(data+pos),len));
        pos+=sublen;
    }
    return ans;
}

bool imageShared::saveShadowToImage(const QByteArrayList &shadow, const QString &savePos, const QTableWidget &cyz)
{
    int w=((m_aSelectBits/8)*8)/m_image.depth();
    int size=w*m_N;
    int h=std::floor(sqrt(size));
    int len=min(w,m_N);
    for(int i=h;i>=len;--i){
        if(!(size%i)){
            w=i;
            h=size/i;
            break;
        }
    }
    QImage shadowimage=QImage(w, h,m_image.format());
    for(int i=0;i<participatorCount;++i){
        memcpy(shadowimage.bits(),shadow[i].constData(),shadow[i].size());
        auto pos=savePos+"/"+cyz.item(i,2)->text();
        QFile file{pos};
        if(!file.open(QIODevice::ReadWrite)){
            return false;
        }
        if(!shadowimage.save(&file)){
            file.close();
            return false;
        }
        file.close();
    }
    return true;
}

bool imageShared::saveShadowToTxt(const QByteArrayList &newshadow, const QString &savePos, const QTableWidget &cyz)
{
    for(int i=0;i<participatorCount;++i){
        auto pos=savePos+"/"+cyz.item(i,2)->text()+".txt";
        QFile file{pos};
        if(!file.open(QIODevice::ReadWrite)){
            return false;
        }
        if(!file.write(newshadow[i])){
            file.close();
            return false;
        }
        file.close();
    }
    return true;
}

bool imageShared::saveShadowToLSB(const QByteArrayList &shadow, const QString &savePos, const QTableWidget &cyz, const QString &carImagePos)
{
    const QImage image{carImagePos};
    for(int i=0;i<participatorCount;++i){
        auto pos=savePos+"/"+cyz.item(i,2)->text()+".png";
        QImage temImage=SteganographyOnImage::imbedded(image,shadow[i],SteganographyOnImage::LSB);
        if(temImage.isNull()){
            return false;
        }
        temImage.save(pos);
    }
    return true;
}

bool imageShared::saveShadowToEMD(const QByteArrayList &shadow, const QString &savePos, const QTableWidget &cyz, const QString &carImagePos)
{
    const QImage image{carImagePos};
    for(int i=0;i<participatorCount;++i){
        auto pos=savePos+"/"+cyz.item(i,2)->text()+".png";
        QImage temImage=SteganographyOnImage::imbedded(image,shadow[i],SteganographyOnImage::LSB);
        if(temImage.isNull()){
            return false;
        }
        temImage.save(pos);
    }
    return true;
}

bool imageShared::getSystemParameters(int L)
{
    if(m_image.isNull()){
        return false;
    }
    m_L=L;
    auto depth=m_image.depth();
    BIGNUM* p = BN_new();
    EC_GROUP_get_curve(m_group.get(),p,nullptr,nullptr,nullptr);
    int pBits=BN_num_bits(p);
    BN_free(p);
    m_k=pBits/depth-(pBits%depth ? 0 : 1);
    int n=m_image.width()*m_image.height();
    m_N=n/(m_k*m_L)+(n%(m_k*m_L)?1:0);
    m_aSelectBits=m_k*depth;
    return true;
}
